
package com.ctrip.corp.obt.shard.sql.mybatis.interceptor.inner;

import com.ctrip.corp.obt.generic.core.context.TenantContext;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.ExceptionUtils;
import com.ctrip.corp.obt.generic.utils.StringPool;
import com.ctrip.corp.obt.shard.holder.InnerPluginIgnoreHolder;
import com.ctrip.corp.obt.shard.sql.parser.AbstractSqlParserSupport;
import com.ctrip.corp.obt.shard.sql.parser.tenant.TenantHandler;
import com.ctrip.corp.obt.shard.utils.ClassUtils;
import com.ctrip.corp.obt.shard.utils.PluginUtils;
import com.ctrip.corp.obt.shard.utils.PropertiesWrapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.NotExpression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.AllColumns;
import net.sf.jsqlparser.statement.select.AllTableColumns;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.LateralSubSelect;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SubJoin;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.statement.select.ValuesList;
import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Stack;
import java.util.stream.Collectors;

/**
 * 租户 SQL InnerInterceptor（ TenantId 行级 ）
 *
 * @author marco.zheng
 * @since 2022-02-10
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Slf4j
@SuppressWarnings({"rawtypes"})
public class TenantInnerPlugin extends AbstractSqlParserSupport implements InnerPlugin {

    private TenantHandler tenantHandler;


    /**
     * 根据当前租户数据源个数判别是否需要开启该配置
     * @param executor      Executor(可能是代理对象)
     * @param ms            MappedStatement
     * @param parameter     parameter
     * @param rowBounds     rowBounds
     * @param resultHandler resultHandler
     * @param boundSql      boundSql
     * @return
     * @throws SQLException
     */
    @Override
    public boolean skipPluginQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        return whetherDoSkipPlugin(TenantInnerPlugin.class);
    }

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        if (TenantContext.getTenantId() == null) {
            if (log.isDebugEnabled()) {
                log.debug("No tenant id was found, will skip beforeQuery");
            }
            return;
        }
        if (InnerPluginIgnoreHolder.willIgnoreTenant(ms.getId())) {
            return;
        }
        PluginUtils.DSBoundSql mpBs = PluginUtils.dsBoundSql(boundSql);
        mpBs.sql(parserSingle(mpBs.sql(), null));
        log.info("TenantPlugin finished: {}", mpBs.sql());
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        if (whetherDoSkipPlugin(TenantInnerPlugin.class)) {
            return;
        }
        PluginUtils.DSStatementHandler dsSh = PluginUtils.dsStatementHandler(sh);
        MappedStatement ms = dsSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            if (InnerPluginIgnoreHolder.willIgnoreTenant(ms.getId())) {
                return;
            }
            PluginUtils.DSBoundSql mpBs = dsSh.dsBoundSql();
            mpBs.sql(parserMulti(mpBs.sql(), null));
            log.info("TenantPlugin finished: {}", mpBs.sql());
        }
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        if (TenantContext.getTenantId() == null) {
            if (log.isDebugEnabled()) {
                log.debug("No tenant id was found, will skip processSelect");
            }
            return;
        }
        processSelectBody(select.getSelectBody());
        List<WithItem> withItemsList = select.getWithItemsList();
        if (!CollectionUtils.isEmpty(withItemsList)) {
            withItemsList.forEach(this::processSelectBody);
        }
    }

    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        if (TenantContext.getTenantId() == null) {
            if (log.isDebugEnabled()) {
                log.debug("No tenant id was found, will skip processInsert");
            }
            return;
        }
        if (tenantHandler.ignoreTable(insert.getTable().getName())) {
            // 过滤退出执行
            return;
        }
        List<Column> columns = insert.getColumns();
        if (CollectionUtils.isEmpty(columns)) {
            // 针对不给列名的insert 不处理
            return;
        }
        String tenantIdColumn = tenantHandler.getTenantIdColumn();
//        if (tenantHandler.ignoreInsert(columns, tenantIdColumn)) {
//            // 针对已给出租户列的insert 不处理
//            return;
//        }
        columns.add(new Column(tenantIdColumn));

        List<Expression> duplicateUpdateColumns = insert.getDuplicateUpdateExpressionList();
        if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {
            EqualsTo equalsTo = new EqualsTo();
            equalsTo.setLeftExpression(new StringValue(tenantIdColumn));
            equalsTo.setRightExpression(tenantHandler.getTenantId());
            duplicateUpdateColumns.add(equalsTo);
        }

        Select select = insert.getSelect();
        if (select != null) {
            this.processInsertSelect(select.getSelectBody());
        } else if (insert.getItemsList() != null) {
            ItemsList itemsList = insert.getItemsList();
            if (itemsList instanceof MultiExpressionList) {
                ((MultiExpressionList) itemsList).getExpressionLists().forEach(el -> el.getExpressions().add(tenantHandler.getTenantId()));
            } else {
                ((ExpressionList) itemsList).getExpressions().add(tenantHandler.getTenantId());
            }
        } else {
            throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");
        }
    }

    /**
     * update 语句处理
     */
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        if (TenantContext.getTenantId() == null) {
            if (log.isDebugEnabled()) {
                log.debug("No tenant id was found, will skip processUpdate");
            }
            return;
        }
        final Table table = update.getTable();
        if (tenantHandler.ignoreTable(table.getName())) {
            // 过滤退出执行
            return;
        }
        update.setWhere(this.andExpression(table, update.getWhere()));
    }

    /**
     * delete 语句处理
     */
    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        if (TenantContext.getTenantId() == null) {
            if (log.isDebugEnabled()) {
                log.debug("No tenant id was found, will skip processDelete");
            }
            return;
        }
        if (tenantHandler.ignoreTable(delete.getTable().getName())) {
            // 过滤退出执行
            return;
        }
        delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere()));
    }

    /**
     * delete update 语句 where 处理
     */
    protected BinaryExpression andExpression(Table table, Expression where) {
        //获得where条件表达式
        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(this.getAliasColumn(table));
        equalsTo.setRightExpression(tenantHandler.getTenantId());
        if (null != where) {
            if (where instanceof OrExpression) {
                return new AndExpression(equalsTo, new Parenthesis(where));
            } else {
                return new AndExpression(equalsTo, where);
            }
        }
        return equalsTo;
    }


    /**
     * 处理 insert into select
     * <p>
     * 进入这里表示需要 insert 的表启用了多租户,则 select 的表都启动了
     *
     * @param selectBody SelectBody
     */
    protected void processInsertSelect(SelectBody selectBody) {
        PlainSelect plainSelect = (PlainSelect) selectBody;
        FromItem fromItem = plainSelect.getFromItem();
        if (fromItem instanceof Table) {
            processPlainSelect(plainSelect);
            appendSelectItem(plainSelect.getSelectItems());
        } else if (fromItem instanceof SubSelect) {
            SubSelect subSelect = (SubSelect) fromItem;
            appendSelectItem(plainSelect.getSelectItems());
            processInsertSelect(subSelect.getSelectBody());
        }
    }

    /**
     * 追加 SelectItem
     *
     * @param selectItems SelectItem
     */
    protected void appendSelectItem(List<SelectItem> selectItems) {
        if (CollectionUtils.isEmpty(selectItems)) return;
        if (selectItems.size() == 1) {
            SelectItem item = selectItems.get(0);
            if (item instanceof AllColumns || item instanceof AllTableColumns) return;
        }
        selectItems.add(new SelectExpressionItem(new Column(tenantHandler.getTenantIdColumn())));
    }

    /**
     * 处理 PlainSelect
     * 1. 修改 join 逻辑 ====> 外连、内连查询，在 on 后统一拼接 主表.tenant_id = 副表.tenant_id 用以将副表数据过滤
     * 2. 修改 where 后 tenant_id 字段添加逻辑
     *   2.1. 原逻辑：以左表为主表做 where 条件拼接，不论是左外还是右外，因此，当遇到右外时，以左表为主，就会出现数据遗漏
     *   2.2  修改拼接时机
     */
    protected void processPlainSelect(PlainSelect plainSelect) {
        // 用于存放临时添加where逻辑的表
        Stack<Table> tableStack = new Stack<>();
        FromItem fromItem = plainSelect.getFromItem();
        Expression where = plainSelect.getWhere();
        processWhereSubSelect(where);
        if (fromItem instanceof Table) {
            Table fromTable = (Table) fromItem;
            // 这里要判断是否是主表，如果是右外，这里就不能如此操作
            tableStack.push(fromTable);
        } else {
            processFromItem(fromItem);
        }
        // 判断select中是否存在子查询
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        if (CollectionUtils.isNotEmpty(selectItems)) {
            selectItems.forEach(this::processSelectItem);
        }
        // select后添加tenant_id，防止开发者在构建临时表时未将tenant_id写入，进而直接导致拼接条件时出现 where 临时表.tenant_id = 'xxx' 的报错
        addTenantIdInSelectItems(plainSelect);
        
        List<Join> joins = plainSelect.getJoins();
        if (CollectionUtils.isNotEmpty(joins)) {
            // join处理
            processJoinsV2(joins, tableStack);
        }
        if (CollectionUtils.isNotEmpty(tableStack)) {
            while (!tableStack.isEmpty()) {
                Table tempTable = tableStack.pop();
                if (!tenantHandler.ignoreTable(tempTable.getName())) {
                    plainSelect.setWhere(builderExpression(plainSelect.getWhere(), tempTable));
                }
            }
        }
    }

    /**
     * 处理where条件内的子查询
     * <p>
     * 支持如下:
     * 1. in
     * 2. =
     * 3. >
     * 4. <
     * 5. >=
     * 6. <=
     * 7. <>
     * 8. EXISTS
     * 9. NOT EXISTS
     * <p>
     * 前提条件:
     * 1. 子查询必须放在小括号中
     * 2. 子查询一般放在比较操作符的右边
     *
     * @param where where 条件
     */
    protected void processWhereSubSelect(Expression where) {
        if (where == null) {
            return;
        }
        if (where instanceof FromItem) {
            processFromItem((FromItem) where);
            return;
        }
        if (where.toString().indexOf("SELECT", 1) > -1) {
            // 有子查询
            if (where instanceof BinaryExpression) {
                // 比较符号 , and , or , 等等
                BinaryExpression expression = (BinaryExpression) where;
                processWhereSubSelect(expression.getLeftExpression());
                processWhereSubSelect(expression.getRightExpression());
            } else if (where instanceof InExpression) {
                // in
                // is_delete IN (SELECT is_delete FROM mb_org_project_info WHERE id = 256)
                InExpression expression = (InExpression) where;
                // 以in为分界线，右表达式 (SELECT is_delete FROM mb_org_project_info WHERE id = 256)
                ItemsList itemsList = expression.getRightItemsList();
                // SubSelect 表示子查询类型
                if (itemsList instanceof SubSelect) {
                    processSelectBody(((SubSelect) itemsList).getSelectBody());
                }
            } else if (where instanceof ExistsExpression) {
                // exists
                ExistsExpression expression = (ExistsExpression) where;
                processWhereSubSelect(expression.getRightExpression());
            } else if (where instanceof NotExpression) {
                // not exists
                NotExpression expression = (NotExpression) where;
                processWhereSubSelect(expression.getExpression());
            } else if (where instanceof Parenthesis) {
                Parenthesis expression = (Parenthesis) where;
                processWhereSubSelect(expression.getExpression());
            }
        }
    }

    private void processSelectItem(SelectItem selectItem) {
        if (selectItem instanceof SelectExpressionItem) {
            SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
            if (selectExpressionItem.getExpression() instanceof SubSelect) {
                processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody());
            } else if (selectExpressionItem.getExpression() instanceof Function) {
                processFunction((Function) selectExpressionItem.getExpression());
            }
        }
    }

    /**
     * 处理函数
     * <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>
     * <p> fixed gitee pulls/141</p>
     *
     * @param function
     */
    private void processFunction(Function function) {
        ExpressionList parameters = function.getParameters();
        if (parameters != null) {
            parameters.getExpressions().forEach(expression -> {
                if (expression instanceof SubSelect) {
                    processSelectBody(((SubSelect) expression).getSelectBody());
                } else if (expression instanceof Function) {
                    processFunction((Function) expression);
                }
            });
        }
    }

    /**
     * 处理子查询等
     */
    private void processFromItem(FromItem fromItem) {
        if (fromItem instanceof SubJoin) {
            SubJoin subJoin = (SubJoin) fromItem;
            if (subJoin.getJoinList() != null) {
                processJoins(subJoin.getJoinList());
            }
            if (subJoin.getLeft() != null) {
                processFromItem(subJoin.getLeft());
            }
        } else if (fromItem instanceof SubSelect) {
            SubSelect subSelect = (SubSelect) fromItem;
            if (subSelect.getSelectBody() != null) {
                processSelectBody(subSelect.getSelectBody());
            }
        } else if (fromItem instanceof ValuesList) {
            log.debug("Perform a subquery, if you do not give us feedback");
        } else if (fromItem instanceof LateralSubSelect) {
            LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
            if (lateralSubSelect.getSubSelect() != null) {
                SubSelect subSelect = lateralSubSelect.getSubSelect();
                if (subSelect.getSelectBody() != null) {
                    processSelectBody(subSelect.getSelectBody());
                }
            }
        }
    }


    /**
     * 处理 joins
     *
     * @param joins join 集合
     */
    private void processJoins(List<Join> joins)  {
        //对于 on 表达式写在最后的 join，需要记录下前面多个 on 的表名
        Deque<Table> tables = new LinkedList<>();
        for (Join join : joins) {
            // 处理 on 表达式
            FromItem fromItem = join.getRightItem();
            if (fromItem instanceof Table) {
                Table fromTable = (Table) fromItem;
                // 获取 join 尾缀的 on 表达式列表
                Collection<Expression> originOnExpressions = join.getOnExpressions();
                // 正常 join on 表达式只有一个，立刻处理
                if (originOnExpressions.size() == 1) {
                    processJoin(join);
                    continue;
                }
                // 当前表是否忽略
                boolean needIgnore = tenantHandler.ignoreTable(fromTable.getName());
                // 表名压栈，忽略的表压入 null，以便后续不处理
                tables.push(needIgnore ? null : fromTable);
                // 尾缀多个 on 表达式的时候统一处理
                if (originOnExpressions.size() > 1) {
                    Collection<Expression> onExpressions = new LinkedList<>();
                    for (Expression originOnExpression : originOnExpressions) {
                        Table currentTable = tables.poll();
                        if (currentTable == null) {
                            onExpressions.add(originOnExpression);
                        } else {
                            onExpressions.add(builderExpression(originOnExpression, currentTable));
                        }
                    }
                    join.setOnExpressions(onExpressions);
                }
            } else {
                // 处理右边连接的子表达式
                processFromItem(fromItem);
            }
        }
    }

    /**
     * 重构方法 处理 joins
     * 改动点：1.on后拼接 主表.tenant_id 字段。同时，对于join后根子查询的情况，校验查询字段是否包含tenant_id，防止后续拼接错误
     *       2.用于确定主表，用于后续where条件主表拼接，where将只拼接主表的tenant_id字段
     * 逻辑：左外连接，左表为主表，join后拼接右表信息
     *      右外连接，右表为主表，join后拼接左表信息
     *      ====> on 后副表信息，where后主表信息
     *
     * @param joins join 集合
     * @param primaryTableStack 主表集合
     */
    private void processJoinsV2(List<Join> joins, Stack<Table> primaryTableStack) {
        // 对于 on 表达式写在最后的 join，需要记录下前面多个 on 的表名
        Deque<Table> tables = new LinkedList<>();
        for (Join join : joins) {
            // 处理 join 表信息
            FromItem fromItem = join.getRightItem();
            // 存放副表信息
            Table secondaryTable = handleJoinRightTable(fromItem);
            // 遇到右表进行主表替换操作
            if (join.isRight() && CollectionUtils.isNotEmpty(primaryTableStack)) {
                Table tempTable = primaryTableStack.pop();
                primaryTableStack.push(secondaryTable);
                secondaryTable = tempTable;
            }
            if (fromItem instanceof Table) {
                Table fromTable = (Table)fromItem;
                // 当前表是否忽略
                boolean needIgnore = tenantHandler.ignoreTable(secondaryTable.getName());
                // 表名压栈，忽略的表压入 null，以便后续不处理
                tables.push(needIgnore ? null : fromTable);
            } else {
                // 处理右边连接的子表达式
                processFromItem(fromItem);
                // 确保子查询中包含 tenant_id 字段
                // addTenantIdInSelectItems(((SubSelect)join.getRightItem()).getSelectBody());
            }
            // 获取 join 尾缀的 on 表达式列表
            Collection<Expression> originOnExpressions = join.getOnExpressions();
            // 正常 join on 表达式只有一个，立刻处理
            if (originOnExpressions.size() == 1) {
                // 构造副表 on 表达式
                builderJoinOnExpression(secondaryTable, join);
                continue;
            }
            // 尾缀多个 on 表达式的时候统一处理
            if (originOnExpressions.size() > 1) {
                Collection<Expression> onExpressions = new LinkedList<>();
                for (Expression originOnExpression : originOnExpressions) {
                    Table currentTable = tables.poll();
                    if (currentTable == null) {
                        onExpressions.add(originOnExpression);
                    } else {
                        onExpressions.add(builderExpression(originOnExpression, currentTable));
                    }
                }
                join.setOnExpressions(onExpressions);
            }
        }
    }

    /**
     * 处理联接语句
     */
    protected void processJoin(Join join) {
        FromItem rightItem = join.getRightItem();
        Table fromTable = new Table();
        if (rightItem instanceof Table) {
            fromTable = (Table) join.getRightItem();
        }else if(rightItem instanceof SubSelect){
            // 对于 join 的子查询，也需要进行on处理
            // 1.确保子查询中包含 tenant_id 字段
            addTenantIdInSelectItems(((SubSelect) join.getRightItem()).getSelectBody());
            // 2.构建on条件后的拼接操作
            fromTable.setAlias(rightItem.getAlias());
        }
        builderJoinOnExpression(fromTable, join);
    }

    /**
     *
     *
     * @param fromTable
     * @param join
     */
    protected void builderJoinOnExpression(Table fromTable, Join join) {
        if (tenantHandler.ignoreTable(fromTable.getName())) {
            // 过滤退出执行
            return;
        }
        // 走到这里说明 on 表达式肯定只有一个
        Collection<Expression> originOnExpressions = join.getOnExpressions();
        List<Expression> onExpressions = new LinkedList<>();
        onExpressions.add(builderExpression(originOnExpressions.iterator().next(), fromTable));
        join.setOnExpressions(onExpressions);
}

    /**
     * 处理条件
     */
    protected Expression builderExpression(Expression currentExpression, Table table) {
        EqualsTo equalsTo = new EqualsTo();
        // 判断表是否存在别名，如果存在别名，那么租户字段需要根据此别名做具体操作
        equalsTo.setLeftExpression(this.getAliasColumn(table));
        equalsTo.setRightExpression(tenantHandler.getTenantId());
        if (currentExpression == null) {
            return equalsTo;
        }
        if (currentExpression instanceof OrExpression) {
            // Parenthesis 提供一个(expression) 的表达式，会在表达式的两侧添加括号
            return new AndExpression(new Parenthesis(currentExpression), equalsTo);
        } else {
            return new AndExpression(currentExpression, equalsTo);
        }
    }

    /**
     * 租户字段别名设置
     * <p>tenantId 或 tableAlias.tenantId</p>
     * 
     * 修改逻辑：
     *    考虑不存在别名的情况，对于多表查询 select * from a join b on a.id = b.id where tenant_id = '' 这条sql是有误的
     *    那么就需要在查询中做一种处理，使用tenant_id时，带上表名（当不存在别名时），如：select * from a where a.tenant_id = ''
     *
     * @param table 表对象
     * @return 字段
     */
    protected Column getAliasColumn(Table table) {
        StringBuilder column = new StringBuilder();
        if (table.getAlias() != null) {
            column.append(table.getAlias().getName()).append(StringPool.DOT);
        } else {
            // 没有别名，则默认使用表名
            column.append(table.getName()).append(StringPool.DOT);
        }
        column.append(tenantHandler.getTenantIdColumn());
        return new Column(column.toString());
    }

    /**
     * 不在乎其传入的 table 是否是子查询，只单纯的获取当前表的别名以及其原名
     *
     * @param fromItem
     * @return
     */
    protected Column getAliasColumnFromJoin(FromItem fromItem) {
        // 不是 table 那么必然会是子查询，对于 join 的子查询也必然存在别名
        if (!(fromItem instanceof Table)) {
            Table table = new Table();
            table.setAlias(fromItem.getAlias());
            return getAliasColumn(table);
        }
        return getAliasColumn((Table)fromItem);
    }

    /**
     * 处理 join 中的副表
     *
     * @param rightItem
     */
    private Table handleJoinRightTable(FromItem rightItem) {
        if (rightItem instanceof Table) {
            return (Table) rightItem;
        } else {
            // 处理join的子查询 left join (select * from a) b 这种方式是存在别名的
            if (rightItem.getAlias() != null) {
                Table table = new Table();
                table.setAlias(rightItem.getAlias());
                return table;
            }
        }
        // 最终返回
        return new Table(rightItem.toString());
    }

    /**
     * 处理所有select后字段（不仅仅包含子查询），保证所有临时表的tenant_id都存在
     *
     * 保证 select 后查询字段包含 tenant_id，以便实现
     * (select * from a) k on k.tenant_id = ''
     * (select id ,tenant_id from a) k on k.tenant_id = ''
     *
     * @param selectBody
     */
    private void addTenantIdInSelectItems(SelectBody selectBody) {
        // 先处理单一情况，后续再补充
        if (selectBody instanceof PlainSelect) {
            PlainSelect plainSelect = (PlainSelect)selectBody;
            List<SelectItem> selectItems = plainSelect.getSelectItems();
            // 遍历查询字段，判断是否包含tenant_id
            if (selectItems.stream().anyMatch(item -> item.toString().contains(tenantHandler.getTenantIdColumn())
                || item.toString().contains(StringPool.ASTERISK))) {
                // 如果有 * 或者 tenant_id 字段则不需要添加
                return;
            }
            // 处理子查询
            // 有些语句不存在 from 后的信息 select last_val_id()，当不存在表时，不做任何操作
            if (Objects.isNull(plainSelect.getFromItem())) {
                return;
            }
            selectItems.add(new SelectExpressionItem(this.getAliasColumnFromJoin(plainSelect.getFromItem())));
        }
    }

    @Override
    public void setProperties(Properties properties) {
        PropertiesWrapper.newInstance(properties).whenNotBlank("tenantHandler",
            ClassUtils::newInstance, this::setTenantHandler);
    }

    @Override
    public int order() {
        return -100;
    }
}


